/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "../Types.h"
#include "../TypeMap.h"
#include "../ReferenceTypeMap.h"
#include "../DynamicTypeMap.h"
#include "../TypeHelper.h"

#include "../util/intmap.h"
#include "../util/chunkbuffer.h"

#include "../common/BigEndianUnsignedByte.h"
#include "../common/BigEndianSignedInteger.h"
#include "../common/U8Ascii.h"
#include "../common/U16ArrayByte.h"

#include "TypeTriple.h"
#include "TypeLink.h"
#include "TypeServer.h"
#include "ProtocolTypeMap.h"

void type_server_init( type_server * server, type_library * library, dynamic_type_map * refMap )
{
	server->library = library;
	protocol_type_map_init( &server->pmap, library );
	server->refMap = refMap;
}

static int processMapReverse( type_server * server, type_input_stream * in, type_output_stream * out );
static int processMapReserve( type_server * server, type_input_stream * in, type_output_stream * out );
static int processMap( type_server * server, type_input_stream * in, type_output_stream * out );


int typeServerLink( void * serverData, argot_read msgReader, void * msgReaderData, argot_write msgWriter, void * msgWriterData )
{	
	type_input_stream tmis;
	type_output_stream tmos;
	type_server * server = (type_server *) serverData;
	uint8_t action;
	uint8_t errId;
	
	type_output_init( &tmos, &server->pmap, msgWriter, msgWriterData );
	type_input_init( &tmis, &server->pmap, msgReader, msgReaderData );
	
	type_input_read_named_object( &tmis, &uint8_name );
	action = type_stack_get_u8( type_input_stack( &tmis ) );			
	if ( action == MAP )
	{
		return processMap( server, &tmis, &tmos );
	}
	else if ( action == MAPRES )
	{
		return processMapReserve( server, &tmis, &tmos );
	}
	else if ( action == MAPREV )
	{
		return processMapReverse( server, &tmis, &tmos );
	}
	
	/* return an error array. */
	errId = ERROR;
	type_output_write_named_object( &tmos, &uint8_name, &errId );
	return TYPE_OK;
}

/*
 * MapReverse is the situation where an identifier has
 * already been mapped on the server but the client doesn't
 * know about it.  The client sends the id and recieves
 * the type name and type definition.
 */
static int processMapReverse( type_server * server, type_input_stream * in, type_output_stream * out )
{
	int32_t id;
	type_name * name;
	type_element * structure;
	type_buffer * definition;
	uint8_t msgType;
	u16array array;

	type_input_read_named_object( in, &sint32_name );
	id = type_stack_get_s32( type_input_stack( in ) );
	name = type_map_get_name( server->refMap, id );
	structure = type_map_get_structure( (type_map*) server->refMap, id );

	definition = type_helper_resolve_structure( (reference_type_map*) server->refMap, structure );
	
	msgType = MAPREV;
	type_output_write_named_object( out, &uint8_name, &msgType );
	type_output_write_named_object( out, &u8ascii_name, name->name );

	array.size = definition->size;
	array.data = definition->data;
	type_output_write_named_object( out, &u16binary_name, &array );
	
	return TYPE_OK;
}

/*
 * MapReserve is for situations where the client is attempting
 * to resolve a type which includes its own definition.  To
 * resolve this situation it reserves an ID for the type by
 * sending the name and receiving an ID.  The server may not
 * have the type and return -1.
 */
int processMapReserve( type_server * server, type_input_stream * in, type_output_stream * out )
{
	type_name tname;
	char * name;
	uint8_t msgType;
	int systemId;

	type_input_read_named_object( in, &u8ascii_name );
	name = (char *) type_stack_get_ptr( type_input_stack( in ) );
	tname.name = name;
	tname.hash = 0;

	msgType = MAPRES;
	type_output_write_named_object( out, &uint8_name, &msgType );			
	
	/* First see if we have a type of the same name. */
	systemId = type_library_get_id( server->library, &tname );
	if ( systemId == NOTYPE )
	{
		int id = -1;
		type_output_write_named_object( out, &sint32_name, &id );
	}
	else
	{
		int id;
		
		/* This will find the id and it is is not yet 
		   mapped it will */
		id = type_map_get_id( server->refMap, &tname );
		
		type_output_write_named_object( out, &sint32_name, &id );
		
	}		
	
	return TYPE_OK;
}


static int processMap( type_server * server, type_input_stream * in, type_output_stream * out )
{
	type_name tname;
	char * name;
	u16array * buffer;
	type_buffer definition;
	uint8_t msgType;
	int systemId;

	type_input_read_named_object( in, &u8ascii_name );
	name = (char*)type_stack_get_ptr( type_input_stack( in ) );
	tname.name = name;
	tname.hash = 0;
	type_input_read_named_object( in, &u16binary_name );
	buffer = (u16array*)type_stack_get_ptr( type_input_stack( in ) );;

	definition.size = buffer->size;
	definition.data = buffer->data;
	/* Use the type name to compare the definition that was sent.
	   If they match we can allocate a new id and return.  If
	   they don't match then we can't agree on the type definition
	   and will return an invalid id. */
	
	msgType = MAP;
	type_output_write_named_object( out, &uint8_name, &msgType );			
	
	/* First see if we have a type of the same name. */
	systemId = type_library_get_id( server->library, &tname );
	if ( systemId == NOTYPE )
	{
		int id = -1;
		type_output_write_named_object( out, &sint32_name, &id);
	}
	else
	{
		/* Check the structures are the same. */
	    type_element * structure = type_library_get_structure( server->library, systemId );

	    if ( type_helper_structure_matches( (reference_type_map*) server->refMap, structure, &definition ))
		{
			int id;
			
			id = type_map_get_id( server->refMap, &tname );

			type_output_write_named_object( out, &sint32_name, &id);
			
		}
		else
		{
			int id = -1;
			/* Return an invalid id. */
			type_output_write_named_object( out, &sint32_name, &id);
		}
	}
	
	return TYPE_OK;
}

